package com.cortex.pineapple.fetch;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.codec.binary.Base64;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.os.AsyncTask;
import android.os.AsyncTask.Status;
import android.util.Log;

import com.cortex.pineapple.PineAppleApplication;
import com.cortex.pineapple.fetch.RequestData.CATEGORY_FILTER_TYPE;
import com.cortex.pineapple.fetch.RequestData.FILTER_TYPE;
import com.cortex.pineapple.fetch.RequestData.SOURCE_TYPE;
import com.cortex.pineapple.fetch.RequestData.FILTER_TYPE.FILTER_VALUE;
import com.cortex.pineapple.utilities.MessagesLog;
import com.cortex.pineapple.utilities.ParseJson;
import com.cortex.pineapple.utilities.TestFetchResults;
import com.cortext.pineapple.link.ImageLink;
import com.cortext.pineapple.link.NewsLink;
import com.cortext.pineapple.link.VideoLink;
import com.cortext.pineapple.link.WebLink;


public class SearchBing {

	private StartLinksThread startLinksThread;
	private static String BING_ID = "OWeiKvhWJSLmD0cTW6/VzNV5e/yQ36mZyOGc+00Zya0=";
	private OnFetchBingStateChangedListener onFetchBingStateChangedListener;
	private static String BING_URL = "https://api.datamarket.azure.com/Bing/Search/V1/";
	private static String QUERY = "Query=";
	private static String WRAPPER_DATA = "%27";
	private static String TOP = "&$top=";
	private static String SKIP = "&$skip=";
	private static String FORMAT = "&$format=";
	private static String SOURCES = "Sources=";
	private static String JSON = "JSON";
	private static String IMAGE_FILTERS = "ImageFilters=";
	private static String WEB_FILE_TYPE = "WebFileType=";
	private static String VIDEO_FILTERS = "VideoFilters=";
	private static String NEWS_CATEGORY = "NewsCategory=";
	private static String NEWS_SORT_BY = "NewsSortBy=";


	public void startFetch(RequestData request){
		if(isRunning()){
			startLinksThread.cancel(true);
		}
		startLinksThread = new StartLinksThread();
		startLinksThread.execute(request);
	}
	public void cancelFetching() {
		if(isRunning()){
			startLinksThread.cancel(true);
		}
	}
	public boolean isRunning(){
		return (startLinksThread != null)&&(startLinksThread.getStatus() == Status.RUNNING)?true:false;
	}
	public void setOnFetchBingStateChangedListener(OnFetchBingStateChangedListener onFetchBingStateChangedListener) {
		this.onFetchBingStateChangedListener = onFetchBingStateChangedListener;
	}

	
	private class StartLinksThread extends AsyncTask<RequestData, String, ResponseData>{
		@Override
		protected ResponseData doInBackground(RequestData... arg0) {
			RequestData request = arg0[0];
			String query = request.getQuery().replace(" ", "%20");
			CATEGORY_FILTER_TYPE categoryFilterType = request.getCategoryFilterType();
			List<SOURCE_TYPE> sources = request.getSourcesType();
			int top = request.getTop();
			int skip = request.getSkip();
			HashMap<FILTER_TYPE, FILTER_VALUE> filters = request.getFilters();
			// this from testing read results from file
			try {
				if(PineAppleApplication.isTestingMode()){
					return TestFetchResults.runTesting(request);
				}
				StringBuffer response = new StringBuffer();
				StringBuilder bingUrl = new StringBuilder();;

				bingUrl.append(BING_URL);
				// add category type to url
				bingUrl.append(CATEGORY_FILTER_TYPE.getCategoryFilterType(categoryFilterType) + "?");

				if(categoryFilterType == CATEGORY_FILTER_TYPE.COMPOSITE){
					// add sources to url
					bingUrl.append(SOURCES);
					bingUrl.append(WRAPPER_DATA);
					for (int i = 0; i < sources.size(); i++) {
						if(i == 0){
							bingUrl.append(SOURCE_TYPE.getSourceType(sources.get(i)));
						}
						else{
							bingUrl.append("%2" +  "B" + SOURCE_TYPE.getSourceType(sources.get(i)));
						}
					}
					bingUrl.append(WRAPPER_DATA + "&");
				}
				if(categoryFilterType == CATEGORY_FILTER_TYPE.IMAGES){
					FILTER_VALUE filterValue = filters.get(FILTER_TYPE.IMAGE_TYPE);
					if(filterValue != null){
						String imageFileType = FILTER_VALUE.getValue(filterValue);
						query += "." + imageFileType;
					}
				}
				// add query to url
				bingUrl.append(QUERY + 
						WRAPPER_DATA + query + WRAPPER_DATA);

				// add filters to url
				switch(categoryFilterType){
				case COMPOSITE:
					break;
				case IMAGES:
					addImageFilters(bingUrl,filters);
					break;
				case NEWS:
					addNewsFilters(bingUrl,filters);
					break;
				case VIDEO:
					addVideoFilters(bingUrl,filters);
					break;
				case WEB:
					addWebFilters(bingUrl, filters);
					break;
				default:
					break;

				}
				//https://api.datamarket.azure.com/Bing/Search/V1/Image?Query=%27top%20gear%27&ImageFilters=%27Size%3Asmall%27&$top=15&$skip=0&$format=JSON
				//https://api.datamarket.azure.com/Bing/Search/V1/Image?Query=%27top%20gear%27&ImageFilters=%27Size%3AMeduim%27&$top=15&$skip=0&$format=JSON
				bingUrl.append(TOP + top + SKIP + skip + FORMAT + JSON);

				byte[] accountKeyBytes = Base64.encodeBase64((BING_ID + ":" + BING_ID).getBytes()); // code for encoding found on stackoverflow
				String accountKeyEnc = new String(accountKeyBytes);

				URL url = new URL(bingUrl.toString());
				URLConnection urlConnection = url.openConnection();
				String s1 = "Basic " + accountKeyEnc;
				urlConnection.setRequestProperty("Authorization", s1);
				BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
				String inputLine;

				while ((inputLine = in.readLine()) != null)
					response.append(inputLine);
				in.close();

				ResponseData responseData = parseResponse(request,response.toString());

				return responseData;
			}
			catch (JSONException e) {
				Log.d(MessagesLog.APPLICATION_EXCEPTION, "exception when parsing results" + e.getMessage());
				if(onFetchBingStateChangedListener != null){
					publishProgress(e.getMessage());
				}
			}
			catch (Exception e) {
				Log.d(MessagesLog.APPLICATION_EXCEPTION, "exception when connection to bing url" + e.getMessage());
				if(onFetchBingStateChangedListener != null){
					publishProgress(e.getMessage());
				}
			}
			return new ResponseData(false,
					new ArrayList<WebLink>(),
					new ArrayList<ImageLink>(),
					new ArrayList<VideoLink>(),
					new ArrayList<NewsLink>(),
					null,null,null,null);
		}
		private void addWebFilters(StringBuilder url,HashMap<FILTER_TYPE, FILTER_VALUE> filters){
			if(!filters.isEmpty()){
				url.append("&" + WEB_FILE_TYPE + WRAPPER_DATA  +
						FILTER_VALUE.getValue(filters.get(FILTER_TYPE.FILE_TYPE)) + WRAPPER_DATA);
			}
		}
		private void addImageFilters(StringBuilder url,HashMap<FILTER_TYPE, FILTER_VALUE> filters){
			int counter = 0;
			if(!filters.isEmpty()){
				url.append("&" + IMAGE_FILTERS + WRAPPER_DATA);
			}
			for (FILTER_TYPE filterType : filters.keySet()) {
				if(filterType == FILTER_TYPE.IMAGE_TYPE){
					// image type added on the query
					continue;
				}
				FILTER_VALUE filterValue = filters.get(filterType);
				url.append(FILTER_TYPE.getValue(filterType)+ "%3" +FILTER_VALUE.getValue(filterValue));
				counter++;
				if(counter < filters.size()){
					url.append("%2B");
				}
			}
			if(!filters.isEmpty()){
				url.append(WRAPPER_DATA);
			}
		}
		private void addVideoFilters(StringBuilder url,HashMap<FILTER_TYPE, FILTER_VALUE> filters){
			int counter = 0;
			if(!filters.isEmpty()){
				url.append("&" + VIDEO_FILTERS + WRAPPER_DATA);
			}
			for (FILTER_TYPE filterType : filters.keySet()) {
				FILTER_VALUE filterValue = filters.get(filterType);
				url.append(FILTER_TYPE.getValue(filterType)+ "%3" +FILTER_VALUE.getValue(filterValue));
				counter++;
				if(counter < filters.size()){
					url.append("%2B");
				}
			}
			if(!filters.isEmpty()){
				url.append(WRAPPER_DATA);
			}
		}
		private void addNewsFilters(StringBuilder url,HashMap<FILTER_TYPE, FILTER_VALUE> filters){
			if(!filters.isEmpty()){
				if(filters.containsKey(FILTER_TYPE.NEWS_CATEGORY)){
					url.append("&" + NEWS_CATEGORY + WRAPPER_DATA + 
							FILTER_VALUE.getValue(filters.get(FILTER_TYPE.NEWS_CATEGORY)) + WRAPPER_DATA);
				}
				if(filters.containsKey(FILTER_TYPE.NEWS_DATE)){
					url.append("&" + NEWS_SORT_BY + WRAPPER_DATA + 
							FILTER_VALUE.getValue(filters.get(FILTER_TYPE.NEWS_DATE)) + WRAPPER_DATA);
				}
			}
		}
		private ResponseData parseResponse(RequestData request, String response) throws JSONException{
			List<WebLink> webLinks = new ArrayList<WebLink>();
			List<ImageLink> imageLinks = new ArrayList<ImageLink>();
			List<VideoLink> videoLinks = new ArrayList<VideoLink>();
			List<NewsLink> newsLinks = new ArrayList<NewsLink>();
			boolean isSuccess = true;

			JSONArray jsonArray = ParseJson.parseResults(response.toString());
			for (int i = 0; i < jsonArray.length(); i++) {
				try{
					switch(request.getCategoryFilterType()){
					case IMAGES:
						imageLinks.add(ParseJson.parseImageLink(jsonArray.getJSONObject(i)));
						break;
					case NEWS:
						newsLinks.add(ParseJson.parseNewsLink(jsonArray.getJSONObject(i)));
						break;
					case VIDEO:
						videoLinks.add(ParseJson.parseVideoLink(jsonArray.getJSONObject(i)));
						break;
					case WEB:
						webLinks.add(ParseJson.parseWebLink(jsonArray.getJSONObject(i)));
						break;
					case COMPOSITE:
						JSONArray jsonResults = ParseJson.parseResults(response.toString());
						JSONArray webResults,imageResults,videoResults,newsResults;
						JSONObject result = jsonResults.getJSONObject(0);

						webResults = result.getJSONArray("Web");
						imageResults = result.getJSONArray("Image");
						videoResults = result.getJSONArray("Video");
						newsResults = result.getJSONArray("News");

						webLinks = ParseJson.parseWebLinks(webResults);
						imageLinks = ParseJson.parseImageLinks(imageResults);
						videoLinks = ParseJson.parseVideoLinks(videoResults);
						newsLinks = ParseJson.parseNewsLinks(newsResults);
						break;
					default:
						webLinks.add(ParseJson.parseWebLink(jsonArray.getJSONObject(i)));
						break;

					}
				} catch (JSONException e) {
					Log.d(MessagesLog.APPLICATION_EXCEPTION, "exception when parsing results" + e.getMessage());
					if(onFetchBingStateChangedListener != null){
						publishProgress(e.getMessage());
					}
					isSuccess = false;
				}
			}
			return new ResponseData(isSuccess,webLinks, imageLinks, videoLinks, newsLinks,
					request.getQuery(), 
					request.getCategoryFilterType(),
					request.getFilters(),
					request.getSourcesType());
		}
		@Override
		protected void onPreExecute() {
			if(onFetchBingStateChangedListener != null){
				onFetchBingStateChangedListener.bingTimeLoadStarted();
			}
		}
		@Override
		protected void onProgressUpdate(String... values) {
			if(onFetchBingStateChangedListener != null){
				onFetchBingStateChangedListener.bingTimeLoadCancelled(values[0]);
			}
		}
		@Override
		protected void onPostExecute(ResponseData response) {
			if(onFetchBingStateChangedListener != null){
				onFetchBingStateChangedListener.bingTimeLoadFinished(response);
			}
		}

	}

	public interface OnFetchBingStateChangedListener{
		void bingTimeLoadStarted();
		void bingTimeLoadFinished(ResponseData responeData);
		void bingTimeLoadCancelled(String message);
	}

	
}
